package com.smartfast4j.manager.modules.sys.service.impl;


import cn.hutool.crypto.SecureUtil;
import com.smartfast4j.common.Constants;
import com.smartfast4j.common.bean.R;
import com.smartfast4j.common.component.cache.CacheService;
import com.smartfast4j.common.config.Cachekey;
import com.smartfast4j.common.exception.Smartfast4jException;
import com.smartfast4j.common.util.GUIDUtil;
import com.smartfast4j.manager.common.bean.LoginUserInfo;
import com.smartfast4j.manager.modules.sys.bean.SysUserExt;
import com.smartfast4j.manager.modules.sys.service.SysResourceService;
import com.smartfast4j.manager.modules.sys.service.SysRoleService;
import com.smartfast4j.manager.modules.sys.service.SysUserRoleService;
import com.smartfast4j.manager.modules.sys.service.SysUserService;
import com.smartfast4j.mapper.sys.SysUserMapper;
import com.smartfast4j.pojo.sys.*;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

@Service
public class SysUserServiceImpl implements SysUserService {
    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private CacheService cacheService;

    @Autowired
    private SysUserRoleService sysUserRoleService;

    @Autowired
    private SysResourceService sysResourceService;

    @Autowired
    private SysRoleService sysRoleService;

    private static final Logger logger = LoggerFactory.getLogger(SysUserServiceImpl.class);

    public int countByExample(SysUserExample example) {
        int count = this.sysUserMapper.countByExample(example);
        logger.debug("count: {}", count);
        return count;
    }

    public SysUser selectByPrimaryKey(Long id) {
        return this.sysUserMapper.selectByPrimaryKey(id);
    }

    public List<SysUser> selectByExample(SysUserExample example) {
        return this.sysUserMapper.selectByExample(example);
    }

    public int deleteByPrimaryKey(Long id) {
        return this.sysUserMapper.deleteByPrimaryKey(id);
    }

    public int updateByPrimaryKeySelective(SysUser record) {
        return this.sysUserMapper.updateByPrimaryKeySelective(record);
    }

    public int updateByPrimaryKey(SysUser record) {
        return this.sysUserMapper.updateByPrimaryKey(record);
    }

    public int deleteByExample(SysUserExample example) {
        return this.sysUserMapper.deleteByExample(example);
    }

    public int updateByExampleSelective(SysUser record, SysUserExample example) {
        return this.sysUserMapper.updateByExampleSelective(record, example);
    }

    public int updateByExample(SysUser record, SysUserExample example) {
        return this.sysUserMapper.updateByExample(record, example);
    }

    public int insert(SysUser record) {
        return this.sysUserMapper.insert(record);
    }

    public int insertSelective(SysUser record) {
        return this.sysUserMapper.insertSelective(record);
    }

    public int insertBatchSelective(List<SysUser> records) {
        return this.sysUserMapper.insertBatchSelective(records);
    }

    public int updateBatchByPrimaryKeySelective(List<SysUser> records) {
        return this.sysUserMapper.updateBatchByPrimaryKeySelective(records);
    }

    /**
     * 获取当前请求对象
     *
     * @return
     */
    private HttpServletRequest getRequest() {
        try {
            return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        } catch (Exception e) {
            throw new RuntimeException("获取request失败", e);
        }
    }

    /**
     * 获取当前登录的系统用户
     * @return
     */
    @Override
    public LoginUserInfo getCurrLoginUser() {
        String token = getRequest().getParameter("token");
        //从缓存中获取当前登录的系统用户
        LoginUserInfo loginUser = cacheService.get(Constants.LOGIN_USER.TOKEN_PREFIX+token);
        return loginUser;
    }

    @Override
    public List<String> getUserPermsByUserId(Long userId) {
        return sysUserMapper.getUserPermsByUserId(userId);
    }

    /**
     * 系统用户登录
     * @param username 用户名
     * @param password 密码
     * @return SysUser
     * @throws Exception
     */
    @Override
    public R login(String username, String password){
        //根据用户名查找用户
        SysUserExample userExample = new SysUserExample();
        userExample.createCriteria()
                .andDeleteFlagEqualTo(Constants.DELETE_FLAG.NO)
                .andUsernameEqualTo(username);
        List<SysUser> sysUserList = selectByExample(userExample);
        if(CollectionUtils.isEmpty(sysUserList)){
            //用户名不正确
            return R.fail("用户名或者密码不正确");
        }
        SysUser sysUser = sysUserList.get(0);
        //使用密码+盐进行sha256加密
        password = SecureUtil.sha256(password + sysUser.getSalt());
        logger.info("加密密码：{}",password);
        //密码错误
        if(!password.equals(sysUser.getPassword())) {
            return R.fail("用户名或者密码不正确");
        }
        //账号已经被禁用
        if(sysUser.getStatus()==Constants.USER_STATUS.FROZEN){
            return R.fail("账号已被锁定,请联系管理员");
        }
        //生成一个登录token
        String token = GUIDUtil.genRandomGUID();
        LoginUserInfo loginUserInfo = new LoginUserInfo();
        try {
            BeanUtils.copyProperties(loginUserInfo,sysUser);
        } catch (IllegalAccessException e) {
            logger.error(e.getMessage(), e);
        } catch (InvocationTargetException e) {
            logger.error(e.getMessage(), e);
        }
        //查找用户所属的角色列表
        List<Long> roleIds = sysUserRoleService.getRoleIdList(sysUser.getId());
        loginUserInfo.setRoleIds(roleIds);
        SysRoleExample roleExample = new SysRoleExample();
        roleExample.createCriteria().andIdIn(roleIds);
        List<SysRole> roleList = sysRoleService.selectByExample(roleExample);
        if(CollectionUtils.isNotEmpty(roleList)){
            String roleNames = "";
            for(SysRole sysRole:roleList){
                roleNames+=sysRole.getRoleName()+",";
            }
            roleNames = roleNames.substring(0,roleNames.length()-1);
            loginUserInfo.setRoleNames(roleNames);
        }
        //查找该用户所拥有的权限
        Set<String> permsSet = sysResourceService.getUserPerms(sysUser.getId());
        loginUserInfo.setPerms(permsSet);
        saveLoginUser(token,loginUserInfo);//保存登录用户信息
        //登录成功，返回登录token
        Map<String,Object> dataMap = new HashMap<>();
        dataMap.put("token",token);
        return R.ok(dataMap);
    }

    @Override
    public void logout(String token) {
        LoginUserInfo loginUser = cacheService.get(Constants.LOGIN_USER.TOKEN_PREFIX+token);
        if(loginUser != null){
            cacheService.remove(Constants.LOGIN_USER.USERNAME_PREFIX+loginUser.getUsername());//移除缓存中的token
            cacheService.remove(Constants.LOGIN_USER.UID_PREFIX+loginUser.getId());//移除缓存中的token
        }
        //注销，删除缓存中的用户信息
        cacheService.remove(Constants.LOGIN_USER.TOKEN_PREFIX+token);
    }

    @Override
    public int updatePassword(Long sysLoginUserId, String password, String newPassword) {
        SysUser sysUser = selectByPrimaryKey(sysLoginUserId);
        //sha256加密
        //使用密码+盐进行sha256加密
        password = SecureUtil.sha256(password + sysUser.getSalt());
        //旧密码正确后才执行修改密码操作
        if(sysUser.getPassword().equals(password)) {
            //修改密码的同时，把用于密码加密的盐也一起修改
            //生成20位长度的随机数，用作密码加密的盐
            String salt = RandomStringUtils.randomAlphanumeric(20);
            //将密码使用sha256加密
            sysUser.setPassword(SecureUtil.sha256(newPassword + salt));
            sysUser.setSalt(salt);
            sysUser.setUpdateUser(sysLoginUserId);
            sysUser.setUpdateTime(new Date());
            return updateByPrimaryKeySelective(sysUser);
        }
        return 0;
    }

    /**
     * @Transactional 注解的方法，将支持事务。如果注解在类上，则整个类的所有方法都默认支持事务。
     * @param sysUserExt
     */
    @Transactional //开启事务（涉及到多表操作）
    @Override
    public void save(SysUserExt sysUserExt) {
        SysUser sysUser = new SysUser();
        try {
            BeanUtils.copyProperties(sysUser, sysUserExt);
            sysUser.setCreateTime(new Date());
            sysUser.setUpdateTime(new Date());
            sysUser.setDeleteFlag(Constants.DELETE_FLAG.NO);
            //生成20位长度的随机数，用作密码加密的盐
            String salt = RandomStringUtils.randomAlphanumeric(20);
            //将密码和盐使用sha256加密
            sysUser.setPassword(SecureUtil.sha256(sysUser.getPassword() + salt));
            sysUser.setSalt(salt);
            //保存系统用户，保存成功后会返回主键的值
            insertSelective(sysUser);
            if (StringUtils.isNotEmpty(sysUserExt.getRoleIds())) {
                List<Long> roleIdList = new ArrayList<>();
                String[] idArr = sysUserExt.getRoleIds().split(",");
                for (String id : idArr) {
                    roleIdList.add(Long.parseLong(id));
                }
                sysUserExt.setRoleIdList(roleIdList);
                //保存用户与角色关系
                sysUserRoleService.save(sysUser.getId(), sysUserExt.getRoleIdList());
            }
        } catch (IllegalAccessException e) {
            logger.error(e.getMessage(), e);
        } catch (InvocationTargetException e) {
            logger.error(e.getMessage(), e);
        }
    }

    @Transactional //开启事务（涉及到多表操作）
    @Override
    public void update(SysUserExt sysUserExt) {
        SysUser sysUser = new SysUser();
        try {
            if(StringUtils.isBlank(sysUserExt.getPassword())){
                sysUserExt.setPassword(null);
            }else {
                //修改密码的同时，把用于密码加密的盐也一起修改
                //生成20位长度的随机数，用作密码加密的盐
                String salt = RandomStringUtils.randomAlphanumeric(20);
                //将密码使用sha256加密
                sysUserExt.setPassword(SecureUtil.sha256(sysUserExt.getPassword() + salt));
                sysUserExt.setSalt(salt);
            }
            //相同属性复制
            BeanUtils.copyProperties(sysUser,sysUserExt);
            sysUser.setUpdateTime(new Date());
            //修改系统用户
            updateByPrimaryKeySelective(sysUser);
            if(StringUtils.isNotEmpty(sysUserExt.getRoleIds())){
                List<Long> roleIdList = new ArrayList<>();
                String[] idArr = sysUserExt.getRoleIds().split(",");
                for(String id : idArr){
                    roleIdList.add(Long.parseLong(id));
                }
                sysUserExt.setRoleIdList(roleIdList);
            }
            //保存用户与角色关系
            sysUserRoleService.save(sysUser.getId(),sysUserExt.getRoleIdList());
        } catch (IllegalAccessException e) {
            logger.error(e.getMessage(),e);
        } catch (InvocationTargetException e) {
            logger.error(e.getMessage(), e);
        }
    }

    @Transactional //开启事务（涉及到多表操作）
    @Override
    public void delete(Long deleteUserId,List<Long> idList) {
        SysUserExample userExample = new SysUserExample();
        userExample.createCriteria().andIdIn(idList);
        SysUser sysUser = new SysUser();
        sysUser.setDeleteFlag(Constants.DELETE_FLAG.YES);
        sysUser.setUpdateUser(deleteUserId);
        sysUser.setUpdateTime(new Date());
        updateByExampleSelective(sysUser,userExample);//逻辑删除

        //删除用户与角色关系
        SysUserRoleExample userRoleExample = new SysUserRoleExample();
        userRoleExample.createCriteria().andUserIdIn(idList);
        sysUserRoleService.deleteByExample(userRoleExample);//物理删除
    }

    @Override
    public void forbid(Long loginUserId, List<Long> idList) {
        SysUserExample userExample = new SysUserExample();
        userExample.createCriteria().andIdIn(idList);
        SysUser sysUser = new SysUser();
        sysUser.setUpdateUser(loginUserId);
        sysUser.setUpdateTime(new Date());
        sysUser.setStatus(Constants.USER_STATUS.FROZEN);//禁用
        updateByExampleSelective(sysUser,userExample);
    }

    @Override
    public void enable(Long loginUserId, List<Long> idList) {
        SysUserExample userExample = new SysUserExample();
        userExample.createCriteria().andIdIn(idList);
        SysUser sysUser = new SysUser();
        sysUser.setUpdateUser(loginUserId);
        sysUser.setUpdateTime(new Date());
        sysUser.setStatus(Constants.USER_STATUS.NORMAL);//启用
        updateByExampleSelective(sysUser,userExample);
    }

    @Override
    public void resetPwd(Long loginUserId, Long id, String newPassword) {
        SysUser sysUser = new SysUser();
        sysUser.setId(id);
        sysUser.setUpdateUser(loginUserId);
        sysUser.setUpdateTime(new Date());
        //生成20位长度的随机数，用作密码加密的盐
        String salt = RandomStringUtils.randomAlphanumeric(20);
        //将密码使用sha256加密
        sysUser.setPassword(SecureUtil.sha256(newPassword+salt));
        sysUser.setSalt(salt);
        updateByPrimaryKeySelective(sysUser);
    }

    @Override
    public SysUser getSysUser(Long id) {
        SysUser sysUser = cacheService.get(Cachekey.Sys.USER+id);
        if(sysUser==null){
            sysUser = selectByPrimaryKey(id);
            //缓存，方便下次使用
            cacheService.set(Cachekey.Sys.USER+sysUser.getId().toString(),sysUser);
        }
        return sysUser;
    }

    @Override
    public void updatePwd(Long loginUserId, String nowPassword, String newPassword) {
        SysUser sysUser = selectByPrimaryKey(loginUserId);
        nowPassword = SecureUtil.sha256(nowPassword+sysUser.getSalt());
        if(!nowPassword.equals(sysUser.getPassword())){
            throw new Smartfast4jException("原始密码不正确");
        }
        //修改密码的同时，把用于密码加密的盐也一起修改
        //生成20位长度的随机数，用作密码加密的盐
        String salt = RandomStringUtils.randomAlphanumeric(20);
        //将密码使用sha256加密
        sysUser.setPassword(SecureUtil.sha256(newPassword+salt));
        sysUser.setSalt(salt);
        updateByPrimaryKeySelective(sysUser);
    }

    @Override
    public void updateUserInfo(SysUser sysUser) {
        sysUser.setUpdateTime(new Date());
        sysUser.setUpdateUser(sysUser.getId());
        updateByPrimaryKeySelective(sysUser);
        //更新用户缓存
        LoginUserInfo loginUserInfo = getCurrLoginUser();
        loginUserInfo.setRealName(sysUser.getRealName());
        loginUserInfo.setCellphone(sysUser.getCellphone());
        loginUserInfo.setEmail(sysUser.getEmail());
        String token = cacheService.get(Constants.LOGIN_USER.UID_PREFIX+loginUserInfo.getId());
        //更新登录用户缓存信息
        cacheService.set(Constants.LOGIN_USER.TOKEN_PREFIX+token,loginUserInfo);
    }

    /**
     * 保存登录用户信息
     */
    private void saveLoginUser(String token,LoginUserInfo loginUserInfo){
        //将登录用户信息放到缓存(以token为key)
        cacheService.set(Constants.LOGIN_USER.TOKEN_PREFIX+token,loginUserInfo);
        //将登录token放到缓存(以用户名为key)
        cacheService.set(Constants.LOGIN_USER.USERNAME_PREFIX+loginUserInfo.getUsername(),token);
        //将登录token放到缓存(以用户id为key)
        cacheService.set(Constants.LOGIN_USER.UID_PREFIX+loginUserInfo.getId(),token);
    }
}